/*-------------------------------------------------------------------------
   Copyright (C) 2010 One Laptop per Child

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   In other words, you are welcome to use, share and improve this program.
   You are forbidden to forbid anyone else to use, share and improve
   what you give them.   Help stamp out software-hoarding!

   As a special exception, you may use this file as part of a free software
   library for the XO of the One Laptop per Child project without restriction.
   Specifically, if other files instantiate
   templates or use macros or inline functions from this file, or you compile
   this file and link it with other files to produce an executable, this
   file does not by itself cause the resulting executable to be covered by
   the GNU General Public License.  This exception does not however
   invalidate any other reasons why the executable file might be covered by
   the GNU General Public License.
-------------------------------------------------------------------------*/

#define SHOW_MPPT_STATE_DEBUG 0

#include <stdio.h>
#include "cc_compat.h"
#include "sdi.h"
#include "timer.h"
#include "adc.h"
#include "onewirebatt.h"
#include "battery.h"
#include "charger.h"
#include "ac.h"
#include "mppt.h"
#include "io.h"

// TODO: refactor so these are used with accessor function rather than global
unsigned char 	xdata power_limit;
int				xdata mppt_hwlimit_thresh;
int				xdata mppt_loop_delay;
unsigned char	mppt_flags;

static unsigned char	xdata mppt_minpower_limit;
static unsigned char 	xdata mppt_maxpower_limit;
static unsigned char 	xdata start_power_limit;
static unsigned char	xdata mppt_state;
static unsigned char	xdata mppt_avg_cnt;
static unsigned char	xdata mppt_verify_delta;
static unsigned char	xdata mppt_prev_limit;
static unsigned char	xdata mppt_prev_mode;
static unsigned char	xdata mppt_peak_count;
static unsigned char 	xdata mppt_step_size;

static unsigned int		xdata mppt_timer;

static int 		xdata mppt_relI_diff;
static int 		xdata mppt_loop_timer;
static unsigned char	xdata squeal_return_state;
static int 		xdata oscillation;
static int 		xdata oscillation_prev;
static int 		xdata squeal_delay;
static int 		xdata squeal_thresh;
static int		xdata min_Ibatt_diff;
static unsigned char 	xdata min_Psystem_limit;
static unsigned char 	xdata mppt_mode;
static int		xdata mppt_Vin_1;
static int		xdata mppt_Vin_2;
static int		xdata Amon;
static unsigned char	xdata mppt_pwrdecrease_count;
static int		xdata mppt_absI_diff;

static long 		xdata mppt_I1;
static long 		xdata mppt_I2;
static long		xdata mppt_prev_I1;
static long		xdata mppt_hwrestart_count;
static unsigned long 	xdata mppt_Isys_peak;

#if 0
// dummy count of AC insertion interrupts, to satisfy squeal checks
int get_AC_IN_count(void)
{
	return 0;
}
#endif

#define MPPT_OFF						0
#define MPPT_INIT						1
#define MPPT_IDLE						2
//#define MPPT_START_SQUEAL_CHECK 		3
//#define MPPT_SQUEAL_CHECK				4
#define MPPT_CHECK_EXT_PWR_PRESENT		5
//#define MPPT_START_CHECK_ENABLE_CHARGE	6
//#define MPPT_CHECK_PAVAILABLE			7
#define MPPT_CHECK_SYSTEM_STATS			8
#define MPPT_SQUEAL						9
#define	MPPT_LOOP_DELAY					10
#define MPPT_LOOP_INIT					11
#define MPPT_DIFF_IBAT					12
#define MPPT_CLIMB_ADJUST				13
#define MPPT_VERIFY_ADJUST				14
#define MPPT_CHANGE_HILLSIDE			15
#define MPPT_LOOP_DELAY_INIT			16
//#define MPPT_VIN_1						17
//#define MPPT_VIN_2						18
//#define MPPT_STARTUP_VCHECK				19
#define	MPPT_START						20
#define	MPPT_LIMIT_V2_START				21
//#define MPPT_START_dV_CHECK				22
#define MPPT_PLV2_INITCHECK_DELAY		23
#define MPPT_PLV2_PWRCHECK				24
#define MPPT_PLV2_SCAN_INIT				25
#define MPPT_PLV2_SCAN_DELAY			26
#define MPPT_PLV2_SCAN_ADJUST			27
//#define MPPT_PLV2_LIMIT_ASSERTED		28
#define MPPT_DISABLED					29
#define MPPT_PLV2_LOOP_INIT				30
#define MPPT_VERIFY_RESTORE				31
#define MPPT_PLV2_LMT_GLITCH				32

#define MPPT_PCT_TO_LIMIT(pct)	((pct*mppt_maxpower_limit)/100)
#define MPPT_LIMIT_TO_PCT(pl)	( (((unsigned int)pl) * 100) / mppt_maxpower_limit )

#define MPPT_Ib_AVG_IN_mA		( ((long)wBAT_CURRENT) * 15625L / 120000L)
#define MPPT_Ib_INST_IN_mA		(Ib_inst_in_mA)
#define MPPT_Vb_IN_mV			( ((long)(wBAT_VOLTAGE)) * 488L / 1600L )
#define MPPT_Wb_IN_mW			( MPPT_Vb_IN_mV * MPPT_Ib_INST_IN_mA / 1000L )

#if 0
#define MPPT_ISYS_AVG_DEPTH		16
#define MPPT_DO_ISYS_AVG		( mppt_Isys_avg = mppt_Isys_avg - (mppt_Isys_avg / MPPT_ISYS_AVG_DEPTH) \
									+ (unsigned long)read_ad_ch(ADC_CHGCUR) )
#endif

#define MPPT_DO_ISYS_PEAK		do { \
	int reading; \
	reading = read_ad_ch(ADC_ADP_CUR_SEN	); \
	if ( reading > mppt_Isys_peak) { mppt_Isys_peak = reading;} \
	} while (0)

#define MPPT_MODE_CLIMB			1
#define MPPT_MODE_VERIFY		2
#define MPPT_MODE_RESTORE		3

#define MPPT_FRONTSIDE			0
#define MPPT_BACKSIDE			1
#define MPPT_ISENSE_BAT			0
#define MPPT_ISENSE_SYS			1

void set_power_limit(int limit)
{
	if (limit>(int)mppt_minpower_limit)	limit = (int)mppt_minpower_limit;
	if (limit<(int)mppt_maxpower_limit)	limit = (int)mppt_maxpower_limit;
	power_limit = (unsigned char)limit;
}

// The polarity of the power limit changes based on
// What type of MPPT setup we have.
// v2 has inverted sesnse from previous MPPT schemes.

void mppt_change_power_limit(int dP)
{
	int limit;

	limit = (int) power_limit;
	limit -= dP;
	set_power_limit (limit);

}

void adjust_squeal_power_limt(int limit)
{
	int new_limit;
	// if we are really high then quickly go low
	if (limit > 0x30)
	{
		new_limit = 0x30;
	}
	else if (limit > 0x2f)
	{
		new_limit = limit-9;
	}
	else if (limit > 0x20)
	{
		new_limit = limit-6;
	}
	else
	{
		new_limit = limit-2;
	}

	set_power_limit(new_limit);
}

long mppt_get_I_in_mA(void)
{

	if ((mppt_flags & mppt_Isense_sys) == 0)
	{
		return Ib_inst_in_mA;
	}
	else
	{
		// System sense resistor is a .025 ohm and the chip output
		// has a gain of 25.  ((3.3V/25)/0.025)/255 = .020705 A/count
//		return ((long)read_ad_ch(ADC_CHGCUR)) * 20705L / 1000L;
//			return ((long)(mppt_Isys_avg/MPPT_ISYS_AVG_DEPTH)) * 20705L / 1000L;
//		return ((long)mppt_Isys_peak) * 20705L / 1000L;
		return charger_adc_to_mA(mppt_Isys_peak);
	}
}

void run_mppt(void)
{
#if SHOW_MPPT_STATE_DEBUG
	{
		static int last_m_s;
		if (mppt_state != last_m_s)
		{
			printf("mppt_state: %d\n", (unsigned int) mppt_state);
			last_m_s = mppt_state;
		}
	}
#endif

	switch (mppt_state)
	{
		case MPPT_DISABLED:
			mppt_flags |= mppt_is_chg_ok;
			mppt_flags &= ~mppt_is_active;

			if ((mppt_flags & mppt_is_enabled) )
			{
				print_tstamp();
				puts("MPPT Enabled");
				mppt_state = MPPT_INIT;
			}
			break;

		case MPPT_INIT:
			mppt_flags &= ~mppt_is_squeal;
			oscillation = 0;
			oscillation_prev = 0;
			squeal_thresh = 0;

			mppt_state = MPPT_IDLE;
			break;

			// These are generic squeal check states.  Squeal check is
			// used in several plaes and this keeps us from duplicating
			// this code serveral times.
#if 0
		case MPPT_START_SQUEAL_CHECK:
			oscillation_prev = get_AC_IN_count();
			mppt_timer = get_ms_timer();
			mppt_state = MPPT_SQUEAL_CHECK;
			break;

		case MPPT_SQUEAL_CHECK:

			MPPT_DO_ISYS_PEAK;

			if (check_timer(mppt_timer,squeal_delay) )
			{
				mppt_flags &= ~mppt_is_squeal;
				oscillation = get_AC_IN_count();

				if ( (oscillation - (oscillation_prev+squeal_thresh)) > 0 )
				{
					if (mppt_flags & mppt_verbose)
					{
						printf(" squeal 0x%02x,0x%04d,%d",(unsigned int)power_limit,
							   (unsigned int) (Vin_avg / MPPT_V_AVG_DEPTH),
							   (oscillation-oscillation_prev)
							  );
					}
					mppt_flags |= mppt_is_squeal;
				}

				mppt_state = squeal_return_state;
			}
			break;
#endif

		case MPPT_IDLE:

			mppt_flags &= ~mppt_is_active;
			mppt_flags &= ~mppt_is_chg_ok;

			// If we don't have a battery then we have nothing
			// to do.
			if ( battery_is_good() && !battery_is_full() )
			{
#if 0
				mppt_state = MPPT_START_SQUEAL_CHECK;
				squeal_thresh = 5;
				squeal_delay = 20;
				squeal_return_state = MPPT_CHECK_EXT_PWR_PRESENT;
#endif
				mppt_state = MPPT_CHECK_EXT_PWR_PRESENT;
			}
			break;

		case MPPT_CHECK_EXT_PWR_PRESENT:

			if ((mppt_flags & mppt_is_squeal) || is_ac_adapter_in())
			{
				mppt_state = MPPT_START;
				break;
			}
			mppt_state = MPPT_IDLE;
			break;

		case MPPT_START:

			print_tstamp();
			puts("PL2: Power Check");
			mppt_state = MPPT_LIMIT_V2_START;
#if 0
			puts("PL2: Power Check");
			mppt_state = MPPT_LIMIT_V2_START;
#endif
			break;

		case MPPT_LIMIT_V2_START:
			set_power_limit(start_power_limit);
			mppt_prev_limit = power_limit;
			mppt_peak_count = 0;
			mppt_flags |= mppt_is_chg_ok;
			mppt_loop_timer = get_ms_timer();
			mppt_state = MPPT_PLV2_PWRCHECK;
			break;

		case MPPT_PLV2_PWRCHECK:
			// Sit here and wait for the battery to start charging
			// then sample the power limit signal.
			// If its not asserted then the power supply connected
			// has enough power to meet our needs.  Don't run mppt.

			if ( battery_is_charging() )
			{
				if ( read_PWR_LMT_ON() )
				{
					// The PWR_LMT_ON signal seems to glitch
					// when charging turns on so add a bit of a
					// delay and then check it again.
					mppt_loop_timer = get_ms_timer();
					mppt_state = MPPT_PLV2_LMT_GLITCH;
				}
			}

			// If either ext power or the battery are removed or the battery
			// is full then go back to the start.
			if (    !is_ac_adapter_in() ||
					!(battery_info & BATTERY_GOOD_FLAG) ||
					battery_is_full()
			   )
			{
				print_tstamp();
				puts("PL2: Going idle");
				set_power_limit(mppt_maxpower_limit);
				mppt_state = MPPT_IDLE;
				break;
			}
			break;

		case MPPT_PLV2_LMT_GLITCH:
			if (check_timer(mppt_loop_timer,100) )
			{
				mppt_state = MPPT_PLV2_PWRCHECK;

				if ( read_PWR_LMT_ON() )
				{
					mppt_loop_timer = get_ms_timer();
					// We are power limited. Go do MPPT
					print_tstamp();
					puts("PL2: Power limit detected. Running MPPT");
					mppt_flags |= mppt_is_active;
					mppt_state = MPPT_PLV2_LOOP_INIT;
				}
			}
			break;
#if 0
		case MPPT_START_dV_CHECK:
			// Start at our lowest possible limit so we can do an input voltage
			// check to see what sort of power supply has
			// been connected.
			power_limit = mppt_minpower_limit;
			// Manually make sure to set the adapter_in flag.
			// In the squeal case this keeps us from bailing
			// out the first time we do LOOP_DELAY state
			// where we check for a real removal event.
#ifdef FIXME
			sPOWER_FLAG.Bit.adapter_in = 1;
#endif
			mppt_flags |= mppt_is_active;
			mppt_hwrestart_count = 0;

			mppt_loop_timer = get_ms_timer();
			// If were are squealing then
			// Jump directly to the loop.
			if (mppt_flags & mppt_is_squeal)
			{
				mppt_state = MPPT_LOOP_DELAY_INIT;
			}
			else
			{
				// Try to do power source detection
				mppt_state = MPPT_VIN_1;
			}
			break;

		case MPPT_VIN_1:
			if (check_timer(mppt_loop_timer,200) )
			{
				mppt_Vin_1 = MPPT_AVG_VIN;

				if (mppt_flags & mppt_verbose)
				{
					print_tstamp();
					printf(" Input dV Check 1: Vn:%d pwr_limit=%x\n",mppt_Vin_1,(unsigned int)power_limit);
				}
				mppt_loop_timer = get_ms_timer();
				power_limit = mppt_maxpower_limit;
				mppt_flags |= mppt_is_chg_ok;
				mppt_state = MPPT_VIN_2;
			}
			break;

		case MPPT_VIN_2:

			squeal_return_state = MPPT_VIN_2;
			squeal_thresh = 5;
			squeal_delay = 5;
			mppt_state = MPPT_START_SQUEAL_CHECK;

			// If we squeal then bail out since we are
			// almost certainly on a PV panel
			if (mppt_flags & mppt_is_squeal)
			{
				mppt_state = MPPT_SQUEAL;
				break;
			}

			if (check_timer(mppt_loop_timer,200) )
			{
				mppt_Vin_2 = MPPT_AVG_VIN;

				if (mppt_flags & mppt_verbose)
				{
					print_tstamp();
					printf(" Input dV Check 2: Vn:%d pwr_limit=%x\n",mppt_Vin_2,(unsigned int)power_limit);
				}
				mppt_loop_timer = get_ms_timer();
				mppt_state = MPPT_STARTUP_VCHECK;
			}
			break;

		case MPPT_STARTUP_VCHECK:

			if (mppt_flags & mppt_verbose)
			{
				print_tstamp();
				printf(" Input dV:%d\n",(mppt_Vin_1 - mppt_Vin_2) );
			}

			// TODO: These values need to be corrected for both board revs
			// and a switch to choose right one for the board
			if ( ((mppt_Vin_1 - mppt_Vin_2) > (10*MPPT_V_AVG_SCALE) )
					|| mppt_Vin_2 < (110*MPPT_V_AVG_SCALE)
			   )
			{
				puts("dV fail. PV or weak adapter");
				power_limit = start_power_limit;
			}
			else
			{
				puts("dV pass. Good adapter");
				power_limit = mppt_maxpower_limit;
			}

			mppt_state = MPPT_LOOP_INIT;
			break;
#endif

		case MPPT_PLV2_LOOP_INIT:
			// With V2 start on the back side of the hill then try to climb
			mppt_I1 = -10000;
			mppt_Isys_peak = 0;
			mppt_mode = MPPT_MODE_CLIMB;
			mppt_flags &= ~mppt_frontside;
			mppt_flags |= mppt_is_active;
			mppt_flags |= mppt_is_chg_ok;
			mppt_pwrdecrease_count = 0;
			mppt_state = MPPT_CHECK_SYSTEM_STATS;
			break;
#if 0
			// Set up all the preconditions for starting the loop
		case MPPT_LOOP_INIT:
			// Start with the initial current low enought that the
			// first comparison always passes.
			mppt_I1 = -10000;
			mppt_Isys_peak = 0;
			mppt_mode = MPPT_MODE_CLIMB;
			mppt_flags |= mppt_frontside;
			mppt_flags |= mppt_is_active;
			mppt_flags |= mppt_is_chg_ok;
			mppt_pwrdecrease_count = 0;
			mppt_state = MPPT_CHECK_SYSTEM_STATS;

			break;
#endif

		case MPPT_CHECK_SYSTEM_STATS:

			mppt_flags |= mppt_is_chg_ok;
			// V2 can always use the battery current sensor
			mppt_flags &= ~mppt_Isense_sys;
#if 0
			// Figure out what current sensor to use
			mppt_flags &= ~mppt_Isense_sys;
#endif
			mppt_state = MPPT_LOOP_DELAY_INIT;
			break;

			// Keeps me from forgetting to set mppt_loop_timer
			// before the call to delay and other per loop
			// items.
		case MPPT_LOOP_DELAY_INIT:
			mppt_loop_timer = get_ms_timer();
			mppt_state = MPPT_LOOP_DELAY;
			mppt_Isys_peak = 0;
			break;

		case MPPT_LOOP_DELAY:

#if 0
			// Loop delay with squeal checking

			squeal_return_state = MPPT_LOOP_DELAY;
			squeal_thresh = 5;
			squeal_delay = 5;
			mppt_state = MPPT_START_SQUEAL_CHECK;

			if (mppt_flags & mppt_is_squeal)
			{
				mppt_state = MPPT_SQUEAL;
				break;
			}
#endif

			// The external power-in detection when MPPT is active
			// has special case where the input voltage has to
			// drop way down before a power removal is
			// registered.  So its ok to check this here.
			// Previously squeal would make power good drop
			// and the whole mppt algorithm would reset.
			// but that makes power_limit == 0xff which makes things
			// worse.
			// Also exit if there is no battery
			if ( !is_ac_adapter_in() ||
					!(battery_info & BATTERY_GOOD_FLAG)
			   )
			{
				set_power_limit( mppt_maxpower_limit) ;
				puts("MPPT: Going idle.");
				mppt_state = MPPT_IDLE;
				break;
			}

			if (check_timer(mppt_loop_timer,mppt_loop_delay) )
			{
				if (mppt_flags & mppt_verbose)
				{
					printf(" %c\n", (mppt_flags & mppt_frontside) ? 'F' : 'B');
				}
				mppt_state = MPPT_DIFF_IBAT;
				break;
			}

			break;

		case MPPT_DIFF_IBAT:
			mppt_I2 	= mppt_get_I_in_mA();
			mppt_Vin_2	= AVG_VIN;

			mppt_absI_diff = mppt_I2 - mppt_I1;

			switch ( mppt_mode )
			{
				case MPPT_MODE_RESTORE:
				case MPPT_MODE_CLIMB:
					mppt_relI_diff = mppt_absI_diff;
					break;

				case MPPT_MODE_VERIFY:
					mppt_relI_diff = mppt_I1 - mppt_I2;
					break;

				default:
					printf("Unknown MPPT mode %u\n",(unsigned int)mppt_mode);
			}

			Amon = charger_adc_to_mA(read_ad_ch(ADC_ADP_CUR_SEN));

			if (mppt_flags & mppt_verbose)
			{
				print_tstamp();
				printf("P:%u PL:%02x I1:%4ld I2:%4ld dI:%5d Ib:%4ld Vn:%5d (%3d) In:%5d dV:%5d Pb:%5ld:",
					   (unsigned int) mppt_peak_count,
					   (unsigned int) power_limit,
					   mppt_I1,
					   mppt_I2,
					   mppt_absI_diff,
					   MPPT_Ib_INST_IN_mA,
					   mppt_Vin_2,
					   VIN_IN_DECI_V,
					   Amon,
					   mppt_Vin_2-mppt_Vin_1,
					   MPPT_Wb_IN_mW
					  );
			}

			switch (mppt_mode)
			{
				case MPPT_MODE_RESTORE:
					// Mode restore is a noop.  Switch to climb
					// We don't update mppt_prev_mode here because for the
					// peak detector we don't want this mode to exist
					mppt_mode = MPPT_MODE_CLIMB;
					mppt_state = MPPT_CLIMB_ADJUST;
					break;

				case MPPT_MODE_CLIMB:

					mppt_state = MPPT_CLIMB_ADJUST;

					if (mppt_relI_diff < min_Ibatt_diff)
					{
						// Switch to verify mode
						mppt_mode = MPPT_MODE_VERIFY;
						mppt_state = MPPT_VERIFY_ADJUST;
						break;
					}
					else
					{
						// If the previous mode was climb then clear the
						// peak counter
						if (mppt_prev_mode == MPPT_MODE_CLIMB)
						{
							mppt_peak_count = 0;
						}
					}

					// Save the n-1 mode for the peak counter.
					mppt_prev_mode = mppt_mode;

					break;

				case MPPT_MODE_VERIFY:
					// If the verify failed then
					// Change hillsides and recheck
					if (mppt_relI_diff < min_Ibatt_diff)
					{
						mppt_peak_count = 0;
						mppt_prev_mode = mppt_mode;
						mppt_state = MPPT_CHANGE_HILLSIDE;
						break;
					}
					else
						// If the verify succeeds then return to our
						// previous power level and restore the previous
						// current measuement.
					{
						mppt_prev_mode = mppt_mode;
						mppt_mode = MPPT_MODE_RESTORE;
						mppt_state = MPPT_VERIFY_RESTORE;
					}
					break;

				default:
					printf("Unknown MPPT mode %u\n",(unsigned int)mppt_mode);

			}
			break;

		case MPPT_VERIFY_RESTORE:
			// Do every thing that a climb state would do but dont
			// actually update the power limit because we have forged
			// the I2 with the saved current.
			// We update the peak counter so to catch
			// repeated climb fail, verify passes patterns.

			mppt_peak_count++;
			set_power_limit (mppt_prev_limit);
			mppt_I1 = mppt_prev_I1;

			// For now leave the voltage alone.
			mppt_Vin_1	= mppt_Vin_2;

			if (mppt_flags & mppt_verbose)
			{
				printf(" R");
			}

			mppt_state = MPPT_CHECK_SYSTEM_STATS;

			break;

		case MPPT_CLIMB_ADJUST:
			// Save data about this point so we can come back to it
			// later
			mppt_prev_limit = power_limit;
			mppt_prev_I1	= mppt_I2;

			mppt_I1 = mppt_I2;
			mppt_Vin_1	= mppt_Vin_2;

			if (mppt_flags & mppt_verbose)
			{
				printf(" C");
			}

			if (mppt_flags & mppt_frontside)
			{
				mppt_change_power_limit( +mppt_step_size );
			}
			else
			{
				mppt_change_power_limit( -mppt_step_size );
			}

			mppt_state = MPPT_CHECK_SYSTEM_STATS;

			break;

		case MPPT_VERIFY_ADJUST:
			// Before we do the verify adjust restore back
			// the the previously saved limit.  This brings things
			// back in case the previous increase crossed the crest of
			// the hill
			set_power_limit(mppt_prev_limit);
			mppt_I1 	= mppt_prev_I1;

			mppt_Vin_1	= mppt_Vin_2;

			if (mppt_flags & mppt_verbose)
			{
				printf(" V");
			}

			if (mppt_flags & mppt_frontside)
			{
				mppt_change_power_limit( -mppt_verify_delta);
			}
			else
			{
				mppt_change_power_limit( +mppt_verify_delta);
			}
			mppt_state = MPPT_CHECK_SYSTEM_STATS;
			break;

		case MPPT_CHANGE_HILLSIDE:
			if (mppt_flags & mppt_verbose)
			{
				printf(" S");
			}
			mppt_flags ^= mppt_frontside;
			mppt_mode = MPPT_MODE_CLIMB;
			mppt_state = MPPT_CLIMB_ADJUST;
			break;

		case MPPT_SQUEAL:

			mppt_flags &= ~mppt_is_squeal;
			adjust_squeal_power_limt((int)power_limit);
			mppt_I1 = mppt_I2;
			mppt_Vin_1	= mppt_Vin_2;
			// If we squeal then its likely we are on the backside
			// of the hill.
			mppt_flags &= ~mppt_frontside;
			mppt_mode = MPPT_MODE_CLIMB;

			mppt_state = MPPT_LOOP_DELAY_INIT;
			break;

		default:
			puts("Unknown MPPT state");
			mppt_state = MPPT_DISABLED;
			break;
	}
}

void mppt_process(void)
{
	apply_power_limit();
	run_mppt();
}

unsigned char mppt_get_limit(void)
{
	return power_limit;
}

void mppt_cmd_read_active(void )
{
	sdi_cmdresp_queue_add((mppt_flags & mppt_is_active) != 0);
}

void mppt_cmd_read_limit(void )
{
	sdi_cmdresp_queue_add(power_limit);
}

void mppt_cmd_set_limit(unsigned char limit)
{
	power_limit = limit;
	printf("Plimit = %02x\n",(unsigned int) limit);
}

void mppt_cmd_enable(char enable)
{
	if (enable)	mppt_flags |= mppt_is_enabled;
	else
	{
		mppt_flags &= ~mppt_is_enabled;
		puts("MPPT Disabled");
		mppt_state = MPPT_DISABLED;
	}
}

void mppt_init_module(void)
{
	mppt_state = MPPT_DISABLED;
	mppt_timer	= 0;
	mppt_flags &= ~mppt_is_active;
	mppt_flags |= mppt_is_chg_ok;
	mppt_flags |= mppt_verbose;
	mppt_flags |= MPPT_ENABLE_DEFAULT;
	min_Ibatt_diff = 1;
	mppt_init_defaults();
}

#ifdef UNUSED
void mppt_set_minpower_limit(unsigned char value)
{
	mppt_minpower_limit = value;
}
#endif

unsigned char mppt_get_minpower_limit(void)
{
	return mppt_minpower_limit;
}

#ifdef UNUSED
void mppt_set_maxpower_limit(unsigned char value)
{
	mppt_maxpower_limit = value;
}
#endif

unsigned char mppt_get_maxpower_limit(void)
{
	return mppt_maxpower_limit;
}

#ifdef UNUSED
void mppt_set_start_power_limit(unsigned char value)
{
	start_power_limit = value;
}

unsigned char mppt_get_start_power_limit(void)
{
	return start_power_limit;
}
#endif

void mppt_init_defaults(void)
{
	// This is a system with the new power_limit scheme
	// sense is inverted from previous schemes
	mppt_hwlimit_thresh 	= 0;
	mppt_maxpower_limit	= 1;
	start_power_limit		= 0;
	mppt_minpower_limit 	= 0xff;
	mppt_loop_delay			= 300;
	mppt_verify_delta	= 0x05;
	mppt_step_size		= 5;
	min_Ibatt_diff 		= 0;
	switch (board_rev)
	{
		case 0xa3:
		case 0xb1:
		case 0xb2:
			break;

		default:
			mppt_flags |= mppt_is_enabled;
	}

#if 0
	// This is a system with the new power_limit scheme
	// sense is inverted from previous schemes
	mppt_hwlimit_thresh 	= 0;
	mppt_maxpower_limit	= 1;
	start_power_limit		= 0;
	mppt_minpower_limit 	= 0xff;
	mppt_loop_delay			= 300;
	mppt_verify_delta	= 0x05;
	mppt_step_size		= 5;
	min_Ibatt_diff 		= 0;
	if (board_rev == 0xb3) mppt_flags |= mppt_is_enabled;
#endif

}

// Help other debug statements play nice with mppt logging
// by prepending a newlne
void mppt_nice_log(void)
{
	if (mppt_flags & (mppt_verbose|mppt_is_active))
	{
		putchar('\n');
	}
}

